classdef XmlElement
   %XmlElement XML结点

   properties
        m_tag      % 结点标签名
        m_content  % 结点内容
        m_attr     % 结点属性
        m_children % 子结点

        m_PRE_MAX
    end

   methods
       function obj = XmlElement(tag, varargin)
       %XmlElement 构造XML结点类的实例
            
            obj.m_tag = tag;
            if nargin >= 2
                obj.m_content = varargin{1};
            else
                obj.m_content = '';
            end
            
            obj.m_PRE_MAX = 1000;
            if nargin >= 3
                obj.m_PRE_MAX = varargin{2};
            end
            

            obj.m_attr = struct;
            obj.m_children = [];
       end
        
       function obj = appendAttri(obj, key, value)
        % 添加结点属性
           obj.m_attr.(key) = value;
       end

       function obj = appendChild(obj, child)
           %append 添加子节点
           assert(isequal(class(child), 'XmlElement'));
           obj.m_children = [obj.m_children, child];
       end
       

       function [tag_elem] = findChild(obj, tag_name)
            
            [res, tag_elem] = findElem(obj, tag_name);
            if ~res
                warning('Cannot find %s', tag_name);
            end
       end
        
       function print(obj)
            fstr = obj.formatStr(0);
            fprintf(fstr);
       end

       function fstr = formatStr(obj, tabSize)
            %formatStr 返回对应的xml格式化字符串
            
            % 标记字符指针
            cnt = 1;
            % 预分配内存
            PRE_MEM = 1000;
            fstr = repmat(' ', 1, PRE_MEM);

            if isempty(obj.m_tag)
                fstr = '';
                return
            end
            
            isleaf = isempty(obj.m_children);

            % 前缀tab
            fstr(cnt:(cnt+2*tabSize-1)) = repmat('\t',1,tabSize);
            cnt = cnt + tabSize*2;
            
            % tag
            fstr(cnt) = '<';
            cnt = cnt + 1;

            len = length(obj.m_tag);
            fstr(cnt:(cnt+len-1)) = obj.m_tag;
            cnt = cnt + len;
            
            % 属性
            attrs = fieldnames(obj.m_attr);
            for i = 1:length(attrs)
                fstr(cnt) = ' ';
                cnt = cnt + 1;
                
                % name="value"
                %fstr(cnt) = '"';
                %cnt = cnt + 1;

                len = length(attrs{i});
                fstr(cnt:(cnt+len-1)) = attrs{i};
                cnt = cnt + len;

                %fstr(cnt) = '"';
                %cnt = cnt + 1;
                
                fstr(cnt) = '=';
                cnt = cnt + 1;

                fstr(cnt) = '"';
                cnt = cnt + 1;

                tmp_clist = char(string( obj.m_attr.(attrs{i})  ));
                len = length(tmp_clist);
                fstr(cnt:(cnt+len-1)) = tmp_clist;
                cnt = cnt + len;
                
                fstr(cnt) = '"';
                cnt = cnt + 1;
                
            end

            fstr(cnt) = '>';
            cnt = cnt + 1;
            
            % 一般如果是叶子结点不会进行换行
            if ~isleaf
                fstr(cnt:(cnt+1)) = '\n';
                cnt = cnt + 2;
            end

            % 子结点
            childCnt = length(obj.m_children);
            for i = 1:childCnt
                child = obj.m_children(i);
                sub_fstr = child.formatStr(tabSize + 1);
                len = length(sub_fstr);
                
                fstr = [fstr, repmat(' ', 1, len)];
                fstr(cnt:(cnt+len-1)) = sub_fstr;
                cnt = cnt + len;
            end

            % 打印内容
            if ~isempty(obj.m_content)
                if ~isleaf
                    fstr(cnt:(cnt+2*(tabSize+1)-1)) = repmat('\t',1,tabSize+1);
                    cnt = cnt + (tabSize+1)*2;
                end
                
                tmp_clist = char( string(obj.m_content) );
                len = length(tmp_clist);
                fstr(cnt:(cnt+len-1)) = tmp_clist;
                cnt = cnt + len;
                
                if ~isleaf
                    fstr(cnt:(cnt+1)) = '\n';
                    cnt = cnt + 2;
                end
            end

            if ~isleaf
                fstr(cnt:(cnt+2*tabSize-1)) = repmat('\t',1,tabSize);
                cnt = cnt + tabSize*2;
            end
            
            fstr(cnt:(cnt+1)) = '</';
            cnt = cnt + 2;

            len = length(obj.m_tag);
            fstr(cnt:(cnt+len-1)) = obj.m_tag;
            cnt = cnt + len;
            
            fstr(cnt:(cnt+2)) = '>\n';
            cnt = cnt + 3;
            
            fstr(cnt:end) = [];
       end
       
      
    
   end
end

function [res, tag_elem] = findElem(elem, tag_name)
    if isempty(elem) || ~isequal(class(elem), 'XmlElement')
        res = false;
        tag_elem = [];
    else
        if isequal(elem.m_tag, tag_name)
            res = true;
            tag_elem = elem;
        else
            for i = 1:length(elem.m_children)
                [res, tag_elem] = findElem(elem.m_children(i), tag_name);
                if res
                    return;
                end
            end
        end
    end
end
